#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <aos/aos.h>

#include "ck_audio.h"

//#define DEBUG_MITRTOS_DISABLE
//#define DEBUG_LOG_MX_ENABLE

#ifdef DEBUG_MITRTOS_DISABLE
#else
#include "nui_things/mit/include/mit_rtos.h"
#include "nui_things/mit/include/debug_mode.h"
#include "nui_things/util/rtf_cal.h"
//#include "nui_things/util/my_fifo.h"
#endif

//#define DEBUG_OFFLINE_ENABLE

#ifdef DEBUG_OFFLINE_ENABLE
//#include "nui_things/mit/include/REC9_aecoff_bfon_5ch_input_3ch_5s.h"
#include "nui_things/kws_BJweather_1_3ch.h"
//#include "nui_things/mit/include/input_3ch.h"
//short voice_file[3];
short * voice_file = kws_BJweather_1_3ch_voice_file;
int voice_file_size  = sizeof(kws_BJweather_1_3ch_voice_file);
int voice_file_index = 0;
int voice_file_going = 0; //wether print the value of fe_out
int voice_file_looper_count = 1;
//int voice_file_frames = 0;

int flag_set_data_frames_after_offlinefile = 1;
int len_ms_set_data_frames_after_offlinefile_long = 20000;
int len_ms_set_data_frames_after_offlinefile_short = 5000;
int len_ms_set_data_frames_after_offlinefile_mutex = 0;
int index_set_data_frames_after_offlinefile = 0;
#endif

#define TAG "AUD"

#define MAX_KWS_DATA_LEN	0x10000		// 64 K
#define MAX_VAD_DATA_LEN	80 * 2 * 16		// 80 ms pcm, this has to be longer than 20 ms processing slice

typedef struct {
	short *data;
	int	len;
} frame_buf_t;

static command_word_handle kws_map __attribute__((aligned(16)));
static char g_kws_data[MAX_KWS_DATA_LEN] __attribute__((aligned(16)));
static char g_vad_data[MAX_VAD_DATA_LEN] __attribute__((aligned(16)));
static int g_vad_data_len;
//static int g_vad_data_finish=0;
static int g_vad_data_should_ep=0;

#ifdef DEBUG_MITRTOS_DISABLE
#else
mit_rtos_listener g_nuithings_listener;
MitRtosConfig g_mit_rtos_config;	   //mit_rtos 初始化配置参数

/* parameters of debug mode*/
unsigned int debug_mode = 0;

//for RTF of dsp_process
rtf_cal_handle rtf_handle_dsp;
int rtf_cal_count = 0;
int rtf_cal_time = 0;

//for RTF of mit_rtos_sdk_process
rtf_cal_handle rtf_handle_dsp_sdk;
int rtf_cal_count_sdk = 0;
int rtf_cal_time_sdk = 0;
int data_len_time_us = 0;//

static int mit_rtos_data_get_main(void * user_data, char * buffer, int len){
	int ret = len;

	return ret;
}

static int mit_rtos_event_main(void * user_data, mit_rtos_event_t event, int dialog_finish){
	//sl_printf( "mit event( %d)\n", event );
	return 0;
}

static int mit_rtos_data_out(void * user_data,  mit_rtos_event_t event, char * buffer, int len){
	return 0;
}
#endif

void audio_init(int frame_sample_count, int vad_enable, int wwv_enable, int vad_endpoint_ignore, int vad_filter, \
				int vad_kws_strategy, MitRtosVadThres *kws_thres, MitRtosVadThres *asr_thres)
{
	int ret_init = 0;
	int ret_start = 0;
#ifdef DEBUG_OFFLINE_ENABLE
	LOGI(TAG, "call mit version nihaoxiaoyang_200k_20191011_140000 test-robustness, offline-bytes=%d \n", voice_file_size);
#else
	LOGI(TAG, "mit version baolabaola_20200528_160000 pmwf 512+128KB\n");
	//LOGI(TAG, "int =%d, long=%d, float=%d longlong=%d\n", sizeof(int), sizeof(long), sizeof(float), sizeof(long long));
#endif

//	g_nui_buffer_asr = malloc(sizeof(short));
//	LOGI(TAG, "call mit ret %d %d %x\n", ret_init, ret_start, g_nui_buffer_asr);
//	free(g_nui_buffer_asr);

#ifdef DEBUG_MITRTOS_DISABLE
#else

	rtf_cal_init(&rtf_handle_dsp, "[dsp]");
	rtf_cal_init(&rtf_handle_dsp_sdk, "[dsp_sdk]");

	//mit_rtos_config_default(&g_mit_rtos_config);
	g_nuithings_listener.on_event_callback = mit_rtos_event_main;
	g_nuithings_listener.need_data_callback = mit_rtos_data_get_main;
	g_nuithings_listener.put_data_callback = mit_rtos_data_out;
	g_nuithings_listener.user_data = NULL;

	g_mit_rtos_config.listener = &g_nuithings_listener;
	g_mit_rtos_config.alg_type = kMitRtosAlgPMWFPG;//kMitRtosAlgBSS;//kMitRtosAlg2MicFloat;
	g_mit_rtos_config.task_enable = 0;
	g_mit_rtos_config.kws_alwayson_enable = 1;
	g_mit_rtos_config.fe_enable = 1;
	g_mit_rtos_config.kws_enable = 1;
	g_mit_rtos_config.vad_enable = vad_enable;
	g_mit_rtos_config.need_data_after_vad = 1;
	g_mit_rtos_config.wwv_enable = wwv_enable;//enable wwv or not.
	g_mit_rtos_config.vad_endpoint_ignore_enable = vad_endpoint_ignore;
	g_mit_rtos_config.voice_filter_type = vad_filter;
	g_mit_rtos_config.log_in_asr_enable = 0;

	g_mit_rtos_config.vad_kws_strategy = vad_kws_strategy;
	g_mit_rtos_config.kws_thres = *kws_thres;
	g_mit_rtos_config.asr_thres = *asr_thres;

	ret_init = mit_rtos_init(&g_mit_rtos_config);

	ret_start = mit_rtos_start();

	/*unsigned int debug_mode = 0;
	unsigned int debug_mode_wwv = DEBUG_MODE_WWV_ALTERNATIVE;
	unsigned int debug_mode_asr_logger = DEBUG_MODE_LOGASR_ON_LITTLE | DEBUG_MODE_LOGASR_ON_MORE | DEBUG_MODE_LOGASR_ON_KWS;
	unsigned int debug_mode_rtf = DEBUG_MODE_RTF_ON_SECTION|DEBUG_MODE_RTF_ON_ALG|DEBUG_MODE_RTF_ON_SDK;

	debug_mode = debug_mode_wwv | debug_mode_asr_logger | debug_mode_rtf;

	mit_rtos_debug_set_mode(debug_mode);*/

#endif

	command_map_init(&kws_map);
	// dcache_writeback(&kws_map, sizeof(command_word_handle));

	//g_fifo_cap_raw = my_fifo_alloc(320 * 16 * 2 * 4);
	// g_frame_sample_count = frame_sample_count;

	LOGI(TAG, "mit ret %d %d\n", ret_init, ret_start);
}

void audio_timeout_config(int vad_active_ms, int silence_ms, int max_ms)
{
	g_mit_rtos_config.vad_endpoint_delay = vad_active_ms;
	g_mit_rtos_config.vad_silencetimeout = silence_ms;
	g_mit_rtos_config.vad_voicetimeout = max_ms;
}

int debug_voice_zero_flag = 0;
int debug_voice_zero_count = 0;

#ifdef DEBUG_MITRTOS_DISABLE
static void mit_status_update(void){

}
#else

static mit_rtos_voice_data g_voice_data;
static MitRtosAudioResult audio_result_main;
static int should_check_asr_data_finish = 0;

static void mit_status_update(void)
{
	int ret = -1;
	int asr_data_is_null = 0;//the asr_data is null of this get

	int au_status = audio_get_status();
	if (au_status == AU_STATUS_READY || au_status == AU_STATUS_FINI)
	{
		return;
	}

	g_voice_data.mode = kMitRtosVoiceModeAsr;
	g_voice_data.available = kVoiceDataInvalid;
	g_voice_data.data = g_vad_data;
	g_voice_data.data_length = MAX_VAD_DATA_LEN;
	ret = mit_rtos_get_voice(&g_voice_data);
	if (0 == ret)
	{
		g_vad_data_len = g_voice_data.data_length;
		/*if (0!=g_vad_data_len) {
			sl_printf("get sdk %d\n", g_vad_data_len);
		}*/
		if (g_vad_data_len<=0) {
#ifdef DEBUG_LOG_MX_ENABLE
			LOGD(TAG, "donot get ASR data");
#endif
			asr_data_is_null = 1;
			if (1==should_check_asr_data_finish) {
				should_check_asr_data_finish = 0;
				asr_data_is_null = 0;

				g_vad_data_should_ep = 1;
#ifdef DEBUG_LOG_MX_ENABLE
				LOGD(TAG, "g_vad_data_finish after vad_end, so set status to FINI");
#endif
				if (au_status != AU_STATUS_WAKE) {
					audio_set_status(AU_STATUS_FINI);
				}

				//g_vad_data_debug_log=1;
			}
		} else {
			/*if (1==g_vad_data_debug_log) {
				g_vad_data_debug_log=0;
				LOGD(TAG, "ASR data g_vad_data_len:%d", g_vad_data_len);
			}*/
		}
	} else {
		LOGE(TAG, "ASR data ret %d",ret);

	}

	if (audio_result_main.vad_state >= 0)
	{
		switch (audio_result_main.vad_state)
		{
		case kMitRotsVadResultStartPoint:
#ifdef DEBUG_LOG_MX_ENABLE
			LOGD(TAG, "get vad startpoint in state asr");
#endif
			//do nothing
			break;
		case kMitRotsVadResultEndPoint:
#ifdef DEBUG_LOG_MX_ENABLE
			LOGD(TAG, "get vad endpoint in state asr");
#endif
			//nls end
			if (1==asr_data_is_null) {
				// endpoint and asr data is null. so stop this dialog
				g_vad_data_should_ep = 1;
#ifdef DEBUG_LOG_MX_ENABLE
				LOGD(TAG, "g_vad_data_finish at endpoint, so set status to FINI");
#endif
				if (au_status != AU_STATUS_WAKE) {
					audio_set_status(AU_STATUS_FINI);
				}
			} else {
				//start the check of data_end
				should_check_asr_data_finish = 1;
			}
			break;
		case kMitRotsVadResultTimeout:
#ifdef DEBUG_LOG_MX_ENABLE
			LOGD(TAG, "get vad timeout in state asr");
#endif
			//nls cancel with err
			if (au_status != AU_STATUS_WAKE) {
				audio_set_status(AU_STATUS_FINI);
			}
			break;
		default:
			LOGE(TAG, "user vad_state(%d) error", audio_result_main.vad_state);
			break;
		}
	}
	//else
	//{
	//	LOGD(TAG, "no event for vad detect");
	//}
}
#endif
int audio_process(short *data_in, int sample_count)
{
	int i;
	// int j;
	int ret = -1;
	static frame_buf_t nui_input_buffer, nui_output_buffer;

	int fe_buffer_out_length = 0;

	int input_data_len = sample_count * 3 * sizeof(short);
	int output_data_len = sample_count * sizeof(short);		// ouput one or two channels

	if (!nui_input_buffer.data || nui_input_buffer.len != input_data_len) {
		aos_free(nui_input_buffer.data);
		nui_input_buffer.data = (short *)aos_malloc_check(input_data_len);
		nui_input_buffer.len = input_data_len;
	}

	if (!nui_output_buffer.data || nui_output_buffer.len != output_data_len) {
		aos_free(nui_output_buffer.data);
		nui_output_buffer.data = (short *)aos_malloc_check(output_data_len);
		nui_output_buffer.len = output_data_len;
	}

	if (debug_mode& DEBUG_MODE_RTF_ON_DSP) {
		data_len_time_us = sample_count*1000/16;// us
		rtf_cal_start_timestamp(&rtf_handle_dsp);
	}

	// reframe input 5 channels data into 3 channels input
	for (i = 0; i < sample_count; i++)
	{
		nui_input_buffer.data[i * 3 + 0] = data_in[i * 5 + 0];
		nui_input_buffer.data[i * 3 + 1] = data_in[i * 5 + 1];
		nui_input_buffer.data[i * 3 + 2] = data_in[i * 5 + 2];
	}

	/* nui_buffer_proc mic 和 ref 交织的数据 */
	if (debug_mode& DEBUG_MODE_RTF_ON_DSP) {
		rtf_cal_start_timestamp(&rtf_handle_dsp_sdk);
	}

	ret = mit_rtos_update_audio((char *)nui_input_buffer.data, input_data_len, (char *)nui_output_buffer.data, &fe_buffer_out_length, &audio_result_main);

	if (debug_mode& DEBUG_MODE_RTF_ON_DSP) {
		rtf_cal_time_sdk = rtf_cal_stop_timestamp(&rtf_handle_dsp_sdk, data_len_time_us);
		if (++rtf_cal_count_sdk >=rtf_cal_get_interval()) {
			rtf_cal_count_sdk = 0;
			rtf_cal_print(&rtf_handle_dsp_sdk);
			rtf_cal_reset(&rtf_handle_dsp_sdk);
		}
	}

	// put output data into input
	for (i = 0; i < sample_count; i++)
	{
		data_in[i * 5 + 4] = nui_output_buffer.data[i];
	}

	if (ret > 10 || ret & 0x01)
	{
		audio_set_status(AU_STATUS_WAKE);
		#ifdef DEBUG_MITRTOS_DISABLE
		#else
		LOGD(TAG, "kws is %s", audio_result_main.data_kws[0].word);
		if (audio_result_main.data_kws_count > 0 && audio_result_main.data_kws[0].do_wwv) {
			ret |= KWS_ID_WWV_MASK;
			LOGD(TAG, "do wwv");
		}
		#endif
	}

	mit_status_update();
	/* 更新状态 */

	if (debug_mode& DEBUG_MODE_RTF_ON_DSP) {
		rtf_cal_time = rtf_cal_stop_timestamp(&rtf_handle_dsp, data_len_time_us);
		if (++rtf_cal_count >=rtf_cal_get_interval()) {
			rtf_cal_count = 0;
			rtf_cal_print(&rtf_handle_dsp);
			rtf_cal_reset(&rtf_handle_dsp);
		}
	}

	return ret;
}

void audio_wakeup_voice_stat(int stat, int buf_delay, int hw_delay)
{
	static int wakeup_audio_playing = 0;

	if (stat != wakeup_audio_playing) {
		mit_rtos_set_playstate(stat? kMitRtosPlayOn : kMitRtosPlayOff, hw_delay, buf_delay);
		LOGD(TAG, "audio stat %d", stat);
		
		wakeup_audio_playing = stat;
	}
}

static int g_audio_status = AU_STATUS_READY;
int audio_get_status()
{
	return g_audio_status;
}

int audio_set_status(int status)
{
	g_audio_status = status;
	return 0;
}

int audio_get_vad()
{
#ifdef DEBUG_MITRTOS_DISABLE
	return -1;
#else
	/* 适配接口定义与该枚举一致 */
	return audio_result_main.vad_state;
#endif
}
char *audio_get_vad_data(int *data_len, int * finished)
{
	*data_len = g_vad_data_len;
	g_vad_data_len = 0;

	*finished = g_vad_data_should_ep;
	if (1==g_vad_data_should_ep) {
		g_vad_data_should_ep = 0;
		LOGD(TAG, "audio_get_vad_data all finished");
	}
#ifdef DEBUG_LOG_MX_ENABLE
	LOGD(TAG, "audio_get_vad_data return %d. length=%d", *finished, *data_len);
#endif
	return *data_len == 0 ? NULL : g_vad_data;
}

command_word_handle *audio_get_kws_map()
{
	return &kws_map;
}

char *audio_get_kws_data(int *data_len)
{
	char *kws_data = NULL;
#ifdef DEBUG_MITRTOS_DISABLE
#else
	g_voice_data.mode = kMitRtosVoiceModeKws;
	g_voice_data.available =kVoiceDataInvalid;
	g_voice_data.data = g_kws_data;
	g_voice_data.data_end = 0;
	g_voice_data.data_length = MAX_KWS_DATA_LEN;

	int ret = mit_rtos_get_voice(&g_voice_data);
	if (ret == 0 && g_voice_data.data_length > 0) {
		*data_len = g_voice_data.data_length;
		kws_data = g_kws_data;
	} 
#endif

	return kws_data;
}

int audio_set_debug_level(unsigned int mode)
{
	debug_mode = mode;
	rtf_cal_count = 0;
	rtf_cal_count_sdk = 0;

	mit_rtos_debug_set_mode(mode);
	return 0;
}

#if 0
int audio_fifo_count(void)
{
	if (g_fifo_cap_raw) {
		return -1;
	}
	return get_fifo_count(g_fifo_cap_raw);
}

int audio_fifo_put(short *mic, short *ref, int sample_count)
{
	int ret;

	if (g_fifo_cap_raw) {
		return -1;
	}

	int put_len = sample_count * sizeof(short) * 2;
	short *buffer_temp_cap_raw = malloc(put_len);
	if (buffer_temp_cap_raw == NULL) {
		return -1;
	}

	for (i = 0; i < sample_count; i++) {
		buffer_temp_cap_raw[4*i]   = mic[2 * i + 0];
		buffer_temp_cap_raw[4*i+1] = mic[2 * i + 1];
		buffer_temp_cap_raw[4*i+2] = ref[2 * i + 0];
		buffer_temp_cap_raw[4*i+3] = ref[2 * i + 1];
	}

	ret = my_fifo_put(g_fifo_cap_raw, buffer_temp_cap_raw, put_len);
	
	free(buffer_temp_cap_raw);
	
	return ret;
}

int audio_fifo_get(void * buffer, unsigned int len_bytes)
{
	if (g_fifo_cap_raw) {
		return -1;
	}
	return my_fifo_get(g_fifo_cap_raw, buffer, len_bytes);
}
#endif
